package org.argus.job.server.gateway.core.loadbalancer;

import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.argus.common.redis.service.RedisService;
import org.argus.job.common.constants.HeaderConstants;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;

import cn.hutool.core.collection.CollectionUtil;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

/**
 * 根据Worker Header IP 来选择Worker实例
 * 
 * @Author 刘文/Cliff Liu
 * @Date 2024/9/16
 */
@Slf4j
public class AppNameHeaderLoadBalancer extends RoundRobinLoadBalancer {

    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

    private AppNameServiceInstanceSelector instanceSelector;

    public AppNameHeaderLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
        String serviceId, AppNameServiceInstanceSelector instanceSelector) {
        super(serviceInstanceListSupplierProvider, serviceId);
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        this.instanceSelector = instanceSelector;
    }

    @SuppressWarnings("rawtypes")
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        List<String> appNames = ((RequestDataContext)request.getContext()).getClientRequest().getHeaders()
            .get(HeaderConstants.APP_NANE_HEADER_NAME);
        List<String> appIds = ((RequestDataContext)request.getContext()).getClientRequest().getHeaders()
            .get(HeaderConstants.APP_ID_HEADER_NAME);
        final String appName = CollectionUtil.isNotEmpty(appNames) ? appNames.get(0) : null;
        final String appId = CollectionUtil.isNotEmpty(appIds) ? appIds.get(0) : null;
        // 没有App 相关信息，使用轮询
        if (StringUtils.isBlank(appName) && StringUtils.isBlank(appId)) {
            return super.choose(request);
        }
        ServiceInstanceListSupplier supplier =
            serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
        return supplier.get(request).next()
            .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances, appName, appId));
    }

    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
        List<ServiceInstance> serviceInstances, String appName, String appId) {
        Response<ServiceInstance> serviceInstanceResponse = instanceSelector.getInstanceResponse(serviceInstances, appName, appId);
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
            ((SelectedInstanceCallback)supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
        }
        return serviceInstanceResponse;
    }
}
